#pragma once
#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include "Thread.hpp"
#include "Task.hpp"
#include "lockGuard.hpp"

static const int max_cap = 10;

template <class T>
class threadPool
{
    typedef threadPool<T> Self;

public:
    static Self *get_instance()
    {
        if (_tp == nullptr)
        {

            // 只有第一次访问单例时会进入
            // 那么绝大多数情况都是不进入的
            // 所以每次都要申请锁再判断是否进入区块, 效率低
            // 双判断, 解决问题

            lockGuard lg(&_tp_mutex);
            if (_tp == nullptr)
            {
                _tp = new Self;
                _tp->start();
            }
        }
        return _tp;
    }

    ~threadPool()
    {
        // 回收线程
        for (auto &thr : _threads)
        {
            thr.join();
        }
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

    // 启动线程池, 所有线程开始待命
    void start()
    {
        for (auto &thr : _threads)
        {
            thr.run();
        }
    }

    void pushTask(const T &in)
    {
        lockGuard lg(&_mutex);

        _tasks.push(in);
        pthread_cond_signal(&_cond);
    }

private:
    // 构造函数不能删除，设为私有，但仅会被get_instance调用一次
    threadPool(const int cap = max_cap) : _threads(cap), _cap(cap)
    {
        // 创建线程, 所有线程处于等待任务的状态
        for (int i = 0; i < _cap; i++)
        {
            // 因为threadRoutine是静态成员函数，所以这里要传入this指针，才能使工作线程找到当前的线程池
            _threads[i] = Thread(i + 1, threadRoutine, this);
        }

        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    // 默认拷贝构造和赋值重载声明为delete删除
    threadPool(const Self &tp) = delete;
    void operator=(const Self &tp) = delete;

    // 工作线程的执行函数，线程池内部私有访问
    // 线程的执行函数默认接口指针类型为void*(*f)(void*)，因此要设为static，除去this指针
    static void *threadRoutine(void *args)
    {
        threadPool *tp = static_cast<threadPool *>(args);
        while (true)
        {
            // 获取任务, 任务队列如果为空，需要等待
            T t = tp->popTask();

            // 处理任务
            t();
            std::cout << "result: " << t.equation() << " " << t.getResult() << std::endl;
        }

        return nullptr;
    }

    // 从任务队列获取任务，线程池内部私有访问
    T popTask()
    {
        T out;

        // 临界区
        {
            lockGuard lg(&_mutex);

            // 任务队列为空时，等待
            while (_tasks.empty())
            {
                pthread_cond_wait(&_cond, &_mutex);
            }
            out = _tasks.front();
            _tasks.pop();
        }

        return out;
    }

private:
    std::queue<T> _tasks;         // 任务队列(大小无限制，采用stl中的自动扩容)
    std::vector<Thread> _threads; // 工作线程

    // 工作线程访问任务队列的互斥量和条件变量
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    int _cap; // 工作线程数量的最大值

    static Self *_tp;
    static pthread_mutex_t _tp_mutex;
};

template <class T>
pthread_mutex_t threadPool<T>::_tp_mutex = PTHREAD_MUTEX_INITIALIZER;

template <class T>
threadPool<T> *threadPool<T>::_tp = nullptr;
